from . import misc
import numpy as np
import scipy.optimize as opt
  

def sigmoid(x):
    return 1.0/(1+np.exp(-x))
   
def derivative(x, y, theta):
    return x.T * (y - sigmoid(x*theta))


class LogLikelihood:
    def __init__(self, x, y):
        (self.x, self.y) = (x, y)

    def __call__(self, theta):
        (x, y) = (self.x, self.y)
        #return -np.sum(y*np.log(sigmoid(x.dot(theta))) + (1-y)*np.log(1-sigmoid(x.dot(theta))))
        return -np.sum(np.log(sigmoid(x.dot(theta))[y==1])) - np.sum(np.log(1-sigmoid(x.dot(theta))[y==0]))

class DerLogLikelihood:
    def __init__(self, x, y):
        (self.x, self.y) = (x, y)

    def __call__(self, theta):
        (x, y) = (self.x, self.y)
        return (sigmoid(x.dot(theta)) - y).dot(x)

class LogisticRegression:
    def __init__(self):
        pass

    def train(self, xs, cs, params={}):
        (xs, self.ranges, self.min_values) = misc.normalize(xs)
        xs = np.hstack((np.ones((len(xs),1)), xs))

        self.c_type = [cs[0]]

        for i in range(1, len(cs)):
            if cs[i] not in self.c_type:
                self.c_type.append(cs[i])

        assert(len(self.c_type) == 2)
        
        _cs = np.zeros(len(cs))
        _cs[np.array(cs) != cs[0]] = 1

        f = LogLikelihood(xs, _cs)
        fd = DerLogLikelihood(xs, _cs)
        #self.w = misc.gradient_descent(mat(ones([shape(xs)[1],1], 'float64')), f, fd)[0]
        self.w = opt.fmin_cg(f, np.zeros(len(xs[0,:]), 'float64'), fd)
        print("W: %s" % (self.w))

    def classify(self, xs):
        return [self._classify_single(x) for x in np.hstack((np.ones((len(xs),1)), (xs - self.min_values) / self.ranges))]

    def _classify_single(self, x):
        return self.c_type[0] if sigmoid(np.dot(x,self.w)) <= 0.5 else self.c_type[1]

def _test_logistic_regression():
    #xs = np.array([[1.0, 1.2], [0.9, 1.1],
    #               [8.9, 9.0], [9.0, 9.1]])

    xs = np.array([
                   [8.9, 9.0], [9.0, 9.1],
                   [4.0, 4.0], [5.0, 5.0]])
    cs = ['a', 'a', 'b', 'b']

    lr = LogisticRegression()
    lr.train(xs, cs)
    #print(lr.classify(np.array([[2.0, 0.1], [9, 9]])))
    print(lr.classify(np.array([[5, 4]])))

if __name__ == "__main__":
    _test_logistic_regression()
